package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	//	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"unsafe"

	"github.com/gliderlabs/ssh"
	"github.com/kr/pty"
)

//var Token string

type contextKey struct {
	name string
}

func setWinsize(f *os.File, w, h int) { //调整term窗口大小使用 不需要动
	syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
}

var ContextToken = &contextKey{"token"} //&contextKey{"token"}

func (dp *SSHProxy) passwdAuth(ctx ssh.Context, password string) bool { //使用密码登陆的验证
	dp.mu.RLock()
	defer dp.mu.RUnlock()
	ctx.SetValue(ContextToken, password)
	if password == "" {
		return false
	}
	return true

}

func (dp *SSHProxy) serve() error {
	ssh.Handle(dp.handle)
	//	srv := &ssh.Server{Addr: ":2222", Handler: nil} //dp.AppInfos.Port
	dp.Conf.load("conf/reg.json")
	srv := &ssh.Server{Addr: dp.Conf.Port, Handler: nil}
	srv.SetOption(ssh.PasswordAuth(dp.passwdAuth)) //使用密码登陆的设置
	return srv.ListenAndServe()
}

func (dp *SSHProxy) AuthContainer(cid string) bool { //验证容器id是否有权限
	dp.mu.RLock()
	defer dp.mu.RUnlock()
	if cid == "" {
		return true
	}
	app := dp.AppInfos            //一个appid对应的appinfo的值
	return app.AuthContainer(cid) //经过AuthContainer方法判断，返回true or false
}

func (dp *SSHProxy) handle(session ssh.Session) { //处理
	dp.mu.Lock()
	appid := session.User()
	//Logs.Printf("%s", appid)
	command, cid := ParseCommandAndCid(appid, session.Command()) //registry里面的命令输入方法
	token := dp.Token[appid]
	token = session.Context().Value(ContextToken).(string)
	dp.Conf.load("conf/reg.json")
	//	var URL = "http://132.122.1.171:3006/console/docker/tokenvalidate?dockerToken=" + token
	//	fmt.Printf("the url is %v", dp.Conf.Port)
	var URL = dp.Conf.Url + token
	response, err := http.Get(URL)
	if err != nil {
		//		log.Fatal(err)
		Logs.SetPrefix("[error]")
		Logs.Println(err)
		io.WriteString(session, fmt.Sprintf("the token is confused\n"))
		return
	}
	content, err := ioutil.ReadAll(response.Body)
	json.Unmarshal(content, dp)
	dp.mu.Unlock()
	if dp.AppInfos.AppID != appid { //判断userid是否正确
		io.WriteString(session, fmt.Sprintf("%s is not allowed\n", appid))
		Logs.SetPrefix("[error]")
		Logs.Printf("Login failed: the user_id is %s,token is %s", appid, token)
		session.Exit(1)
		return
	} else {
		Logs.SetPrefix("[info]")
		Logs.Printf("Login successful: the user_id: %s", appid)
	}

	if len(command) == 0 {
		io.WriteString(session, fmt.Sprintf("command %v is not supported\n", session.Command()))
		Logs.SetPrefix("[error]")
		Logs.Printf("the error command: %v", session.Command())
		session.Exit(1)
		return
	}
	io.WriteString(session, fmt.Sprintf("command is: [%s]\n", strings.Join(command, " ")))
	Logs.SetPrefix("[info]")
	Logs.Printf("Command is %s", command)

	if cid != "" && !dp.AuthContainer(cid) {
		io.WriteString(session, fmt.Sprintf("%s is not allowed\n", cid))
		Logs.SetPrefix("[error]")
		Logs.Printf("%s is not allowed\n", cid)
		session.Exit(1)
		return
	}
	ptyReq, winCh, isPty := session.Pty()
	if !isPty {
		io.WriteString(session, "No PTY requested\n try to use:\n ssh -tt xx.xx.xx.xx command\n")
		session.Exit(1)
		return
	}
	cmd := exec.Command(command[0], command[1:]...)
	cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
	f, err := pty.Start(cmd)
	if err != nil {
		io.WriteString(session, fmt.Sprintf("command error\n %v\n", err))
		Logs.SetPrefix("[error]")
		Logs.Printf("command error: %v", err)
		session.Exit(1)
	}
	go func() { //调整窗口大小
		for win := range winCh {
			setWinsize(f, win.Width, win.Height)
		}
	}()
	go func() { //读输入
		io.Copy(f, session) // stdin
	}()
	io.Copy(session, f) // stdout
}
